package com.atguigu.boot.config;


import com.atguigu.boot.bean.Pet;
import com.atguigu.boot.converter.GuiguMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zp
 * @description: 注入webMvcConfigurer 有两个方式 @bean的方式 实现匿名内部类
 * 或者通过实现WebMvcConfigurer接口实现作为配置类实现
 * @date: 2022/9/16 19:11
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig {
    // 隐藏参数设置 发送不同请求类型 PUT DELETE POST 可以根据隐藏参数自动通过spring的解析对应请求方式的controller
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }


    //1、WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {

            /**
             * 自定义内容协商策略
             * ModelAndView
             * Model
             * View
             * ResponseEntity
             * ResponseBodyEmitter
             * StreamingResponseBody
             * HttpEntity
             * HttpHeaders
             * Callable
             * DeferredResult
             * ListenableFuture
             * CompletionStage
             * WebAsyncTask
             * 有 @ModelAttribute 且为对象类型的
             * @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor；
             *
             * 默认的消息处理器MessageConverter 只有10个
             *
             * 0 - 只支持Byte类型的
             * 1 - String
             * 2 - String
             * 3 - Resource
             * 4 - ResourceRegion
             * 5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
             * 6 - MultiValueMap
             * 7 - true
             * 8 - true
             * 9 - 支持注解方式xml处理的。
             * 内容协商原理：
             * 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
             * 2、返回值处理器调用 handleReturnValue 进行处理
             * 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
             *   1.  利用 MessageConverters 进行处理 将数据写为json
             *      1、内容协商（浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型）
             *      2、服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据，
             *      3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ，看谁能处理？
             *       1、得到MappingJackson2HttpMessageConverter可以将对象写为json
             *       2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
             *  自定义请求处理器 添加了参数处理器和请求头处理器
             * @param configurer
             */
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                //Map<String, MediaType> mediaTypes
                // 配置了参数化的format 对应的参数
                Map<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json", MediaType.APPLICATION_JSON);
                mediaTypes.put("xml", MediaType.APPLICATION_XML);
                mediaTypes.put("gg", MediaType.parseMediaType("application/x-guigu"));
                //指定支持解析哪些参数对应的哪些媒体类型
                ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
//                parameterStrategy.setParameterName("ff");
                //使用自定义的参数化处理器和请求头处理器
                HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();
                // 把参数处理器放刀处理器配置当中 可以放置多个
                configurer.strategies(Arrays.asList(parameterStrategy, headeStrategy));
            }

            // 具体处理返回参数的处理器 添加到处理器集合当中 根据MediaType 类型确定返回参数的处理器类型
            // 自定义的处理器类型需要实现 HttpMessageConverter 接口重写几个方法
            // canRead 是否可读
            // canWrite 是否可写
            // getSupportedMediaTypes 获取处理的 MediaType类型集合
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new GuiguMessageConverter());
            }

            // 自定义Url过滤器 开启矩阵变量的解析 默认是关闭的 重新放入一个自己定义的UrlPathHelper
            // 把setRemoveSemicolonContent设为false
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除；后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            // 自定义model数据解析器 解析请求携带的数据 <input name="pet" value="啊猫,3"/>
            // 自定义了 String映射Pet类型的解析器 springboot自动会加载这个解析器 解析相关联的数据
            // 默认情况下spring中只含有124中数据解析器
            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {

                    @Override
                    public Pet convert(String source) {
                        // 啊猫,3
                        if (!StringUtils.isEmpty(source)) {
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }


//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//
//        UrlPathHelper urlPathHelper = new UrlPathHelper();
//        // 不移除；后面的内容。矩阵变量功能就可以生效
//        urlPathHelper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(urlPathHelper);
//    }
}





